package com.quanyan.ic.video.http.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.ltsopensource.jobclient.JobClient;
import com.github.ltsopensource.jobclient.domain.Response;
import com.quanyan.ic.video.constants.JobConstant;
import com.quanyan.ic.video.http.cache.Hik8710EventDAO;
import com.quanyan.ic.video.http.service.Hik8710EventService;
import com.quanyan.ic.video.http.service.PlaceUnitAlarmService;
import com.quanyan.ic.video.model.hik8710.Hik8710Event;
import com.quanyan.ic.video.model.hik8710.Hik8710PlaceUnitSetting;
import com.quanyan.ic.video.task.TimerTaskJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by zhuo.shi on 2017/8/16.
 * <p/>
 * 海康8710平台移动侦测事件（131331）处理
 */
@Service("131331")
public class Hik8710_131331EventServiceImpl implements Hik8710EventService {

    public static final Logger logger = LoggerFactory.getLogger(Hik8710_131331EventServiceImpl.class);

    @Autowired
    private TimerTaskJob timerTaskJob;
    @Autowired
    private JobClient jobClient;
    @Autowired
    private Hik8710EventDAO hik8710EventDAO;
    @Autowired
    private PlaceUnitAlarmService placeUnitAlarmService;

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void handleEvent(Hik8710Event hik8710Event) {
        logger.info("handle event: type={}, state={}, eventId={}", hik8710Event.getEvent_type(), hik8710Event.getState(), hik8710Event.getEvent_id());
        if (hik8710Event.getEvent_type() != 131331) {
            return;
        }
        String resIndex = hik8710Event.getRes_index();
        Integer eventType = hik8710Event.getEvent_type();
        Hik8710PlaceUnitSetting setting = hik8710EventDAO.get8710EventPlaceUnitSetting(resIndex, eventType);
        if (setting == null) {
            logger.warn("camera [{} {}] not mapping to place_unit {}, ignore", resIndex, eventType, hik8710Event.getRes_name());
            return;
        }
        Byte state = hik8710Event.getState();
        if (state == 0) {        //忽略移动侦测瞬态事件
            return;
        }

        long eventTime = getTimeByStr(hik8710Event.getTime());         //事件时间戳
        String eventId = hik8710Event.getEvent_id();
        int placeId = setting.getPlaceId();             //场馆id
        int placeUnitId = setting.getPlaceUnitId();     //场地id
        long continuousInterval = hik8710EventDAO.get131331EventParam_ContinuousInterval();      //认为是连续事件的最大间隔时间
        long alarmLimtTime = hik8710EventDAO.get131331EventParam_AlarmLimitTime();               //连续事件持续多长时间后告警

        /*每片场地一个队列去保存hik事件 place_id + unit_id -- [event1, event2, event3, ...]
         *每个场馆每天有一个告警hash列表    place_id + day -- | unit_id1 -- alarm        只保存最新一条警告
         *                                                    | unit_id2 -- alarm
         *                                                    | unit_id3 -- alarm
         */

        if (state == 1) {    //开始事件
            logger.info("event state 1: {}", JSON.toJSONString(hik8710Event));
            Hik8710Event prevEvent = hik8710EventDAO.get131331EventTail(placeId, placeUnitId);          //上一个事件
            if (prevEvent != null) {
                if (getTimeByStr(prevEvent.getTime()) > eventTime) {         //上一个事件的事件比当前事件时间大，清空之前的事件列表
                    hik8710EventDAO.clear131331EventList(placeId, placeUnitId);
                    logger.info("preEvent time {} > currentEvent time {}, clear 131331EventList", prevEvent.getTime(), hik8710Event.getTime());
                }
                long prevEventTime = getTimeByStr(prevEvent.getTime());
                if (eventTime - prevEventTime > continuousInterval) {        //当前事件时间 - 上一个事件时间 > 大于最大连续间隔时间，丢弃之前的事件列表
                    hik8710EventDAO.clear131331EventList(placeId, placeUnitId);
                    logger.info("currentEvent time {} - preEvent time {} > {}, clear 131331EventList", hik8710Event.getTime(), prevEvent.getTime(), continuousInterval);
                }
            }
            hik8710EventDAO.append131331Event2Tail(placeId, placeUnitId, hik8710Event);     //将开始事件添加到结尾
            logger.info("append start event to 131331EventList tail, {} {} {}", state, eventId, hik8710Event.getTime());
            if (!hik8710Event.isMock()) {                                //非模拟海康开始事件，启动定时任务
                this.submitStartEventMonitorTask(placeId, placeUnitId, hik8710Event);
            }
        }
        if (state == 2) {     //结束事件
            logger.info("event state 2: {}", JSON.toJSONString(hik8710Event));
            Hik8710Event firstEvent = hik8710EventDAO.get131331EventHead(placeId, placeUnitId);          //第一个事件
            if (firstEvent == null) {                                  //之前没有事件，就收到了结束事件，可能是服务器断网，没有收到海康8710平台推送的开始事件，丢弃此类结束事件
                logger.info("firstEvent is null, ignore this end event, return");
                return;
            }
            long firstEventTime = getTimeByStr(firstEvent.getTime());
            if (!hik8710Event.isMock()) {                               //非模拟海康结束事件，删除定时任务
                this.cancelStartEventMonitorTask(placeId, placeUnitId, eventId);
            }
            boolean unitInOrder = hik8710EventDAO.isOrderTimeSegment(placeId, placeUnitId, eventTime);  //场馆场地是否有正在进行中的订单
            logger.info("placeUnit {} {}, currentTime hasOrder={}", placeId, placeUnitId, unitInOrder);
            if (unitInOrder) {                                           //场地有订单，清空事件列表，删除定时任务，清空告警列表
                hik8710EventDAO.clear131331EventList(placeId, placeUnitId);
                this.cancelStartEventMonitorTask(placeId, placeUnitId, eventId);
                logger.info("has order, clear 131331EventList, cancel startEvent, done");
                return;
            }

            hik8710EventDAO.append131331Event2Tail(placeId, placeUnitId, hik8710Event);     //将结束事件添加到结尾
            logger.info("append end event to 131331EventList tail, {} {} {}", state, eventId, hik8710Event.getTime());

            if (eventTime - firstEventTime >= alarmLimtTime) {          //连续事件持续时间超过告警限制时间，生成或者更新告警
                logger.info("currentEvent time {} - firstEventTime time {} > {}, addOrUpdate alarm", hik8710Event.getTime(), firstEvent.getTime(), alarmLimtTime);
                placeUnitAlarmService.addOrUpdateAlarm(placeId, placeUnitId, hik8710Event, firstEventTime, eventTime);
            } else {
                logger.info("currentEvent time {} - firstEventTime time {} < {}, continue ", hik8710Event.getTime(), firstEvent.getTime(), alarmLimtTime);

            }
        }
    }


    /**
     * 根据事件id，找到匹配的开始事件
     */
    private Hik8710Event findMatchingStartEvent(int placeId, int placeUnitId, String eventId) {
        List<Hik8710Event> events = hik8710EventDAO.get131331EventList(placeId, placeUnitId);
        for (Hik8710Event event : events) {
            if (event.getState() == 1 && event.getEvent_id().equals(eventId)) {
                return event;
            }
        }
        return null;
    }

    /**
     * 提交开始事件是否持续监控任务
     *
     * @param placeId     场馆id
     * @param placeUnitId 场地id
     * @param event       开始事件
     */
    private void submitStartEventMonitorTask(int placeId, int placeUnitId, Hik8710Event event) {
        hik8710EventDAO.setStartEventTag(event.getEvent_id());
        Map<String, String> params = new HashMap<>();
        params.put("placeId", String.valueOf(placeId));
        params.put("placeUnitId", String.valueOf(placeUnitId));
        params.put("event", JSON.toJSONString(event));
        String taskId = JobConstant.START_EVENT_TASK_ID + placeId + "_" + placeUnitId + "_" + event.getTime().replaceAll("-", "").replaceAll(":", "").replaceAll(" ", "");
        long executeTime = System.currentTimeMillis() + hik8710EventDAO.get131331EventParam_AlarmLimitTime();
        timerTaskJob.submitTask(params, taskId, executeTime, 0L);
        logger.info("submit startEvent task: {}, will execute at {}", taskId, sdf.format(new Date(executeTime)));
    }

    /**
     * 取消开始事件是否持续的监控任务
     *
     * @param placeId     场馆id
     * @param placeUnitId 场地id
     * @param eventId     事件id
     */
    private void cancelStartEventMonitorTask(int placeId, int placeUnitId, String eventId) {
        Hik8710Event startEvent = this.findMatchingStartEvent(placeId, placeUnitId, eventId);         //与此结束事件匹配的开始事件
        hik8710EventDAO.delStartEventTag(eventId);
        if (startEvent != null) {
            String taskId = JobConstant.START_EVENT_TASK_ID + placeId + "_" + placeUnitId + "_" + startEvent.getTime().replaceAll("-", "").replaceAll(":", "").replaceAll(" ", "");
            Response resp = jobClient.cancelJob(taskId, JobConstant.NODE_GROUP_TRACKER);
            logger.info("cancel startEvent task: {}, {}, {}", taskId, eventId, resp);
        }
    }

    private long getTimeByStr(String eventTime) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return sdf.parse(eventTime).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return -1;
    }

}
